package main;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;

public class Simulation extends JPanel implements Runnable, MouseListener, MouseMotionListener{
	

	private Thread t;
	private Image i;
	public static final Grid grid = new Grid();
	public static final ArrayList<Swarm_Agent> swarm = new ArrayList<Swarm_Agent>();
	private Artificial_Agent agent;
	private int select_X, select_Y, numClicks = 0, step = 0, vision_range = 2;			
	public static int numAgents = 2;
	private SearchStatus status = SearchStatus.NOTSEARCHING;
	private long start_time, elapsed_time;
	public boolean searching = false;
	private static final long serialVersionUID = 1L;
	public Simulation()
	{
	       addMouseListener(this);
	       addMouseMotionListener(this);
	       setBounds(0,0,1000,600);
	       setLayout(null);
	       t = new Thread(this);
	       t.start();
	}
	public void run()
    {
        
		/*
		 * TODO: I took the liberty of changing the try catch situation here.
		 * When you do a try {} catch (Exception e) { do nothing }
		 * that's called "eating" the exception, and you never find out what's wrong
		 * or how to fix it.
		 */
		
        while(true)
        {
            try {
				Thread.sleep(20);
			} catch (InterruptedException e) {
				
				e.printStackTrace();
			}
            updateSim();
            repaint();
        }
        
     
    }
	
	public void updateSim() {
		
	     
	     if(searching)
	     {
	    	 step++;
	    	 if(GUI.isSwarm)
	    	 {
		    	 for(Swarm_Agent a: swarm)
		    		 a.update();
	    	 }
	    	 else if (agent != null)
	    	 {
	    		 agent.update();
	    	 }
	    	 elapsed_time = System.nanoTime() - start_time;
	     }
	     
	     if(GUI.isSwarm)
	     {
		     int numFailures = 0;
		     for(Swarm_Agent a: swarm)
		     {
		    	 if(a.getSuccess() == SearchStatus.FAILURE)
		    	 {
		    		numFailures++;
		    		if(numFailures == numAgents)
		    		{
		    			searching = false;//none of the agents could find the final location
		    			status = SearchStatus.FAILURE;
		    			grid.clearGrid(false);
		    			swarm.clear();
		    			break;
		    		}
		    	 }
		    	 else if (a.getSuccess() == SearchStatus.SUCCESSFUL)
		    	 {
		    		 searching = false;//at least one agent found the final location
		    		 status = SearchStatus.SUCCESSFUL;
		    		 grid.clearGrid(false);
		    		 swarm.clear();
		    		 break;
		    	 }
		     }
	     }
	     else if (!GUI.isSwarm && agent != null)
	     {
	    	 if(agent.getSuccess() == SearchStatus.FAILURE || agent.getSuccess() == SearchStatus.SUCCESSFUL)
	    	 {
	    		 searching = false;
	    		 status = agent.getSuccess();
	    		 grid.clearGrid(false);
	    		 agent = null;
	    	 }
	     }
	     if(status == SearchStatus.SEARCHING)
	    	 GUI.search_status.setText("Search Status: Searching");
	     else if (status == SearchStatus.NOTSEARCHING)
	    	 GUI.search_status.setText("Search Status: Not Searching");
	     else if(status == SearchStatus.FAILURE)
	    	 GUI.search_status.setText("Search Status: End Location Unreachable");
	     else
	    	 GUI.search_status.setText("Search Status: End Location Found");
	     GUI.number_steps.setText("Steps Taken: "+step);
	     GUI.elapsed_time.setText("Time Taken: "+elapsed_time+" Nanoseconds");
	     
		
	}
	public void paint(Graphics g)
	{
		 i = createImage(getSize().width, getSize().height);
	     Graphics2D g2 = (Graphics2D)i.getGraphics();
	     g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
	     
	     grid.draw(g2);
	     
	     if(searching)
	     {
	    	 if(GUI.isSwarm)
	    	 {
		    	 for(Swarm_Agent a: swarm)
		    		 a.draw(g2);
	    	 }
	    	 else
	    	 {
	    		 agent.draw(g2);
	    	 }
	     }
	     else 
	     { 	
	    	 g2.setColor(new Color (0,255,191));
	    	 g2.fillRect(select_X, select_Y, 20, 20);
	     }
	     
	     
	     g2.dispose();
	     g.drawImage(i, 0,0, this);
	}
	public void createNewMap()
	{
		if (!searching)
		{
			grid.createGrid();
			numClicks = 0;
			status = SearchStatus.NOTSEARCHING;
			swarm.clear();
			agent = null;
		}
	}
	public void setNumberAgents(int agent_number)
	{
		if(GUI.isSwarm)
			numAgents = agent_number;
	}
	public void setVisionRange(int range)
	{
		if(!GUI.isSwarm)
			vision_range = range;
	}
	public void startSearching()
	{
		if(numClicks == 2 && !searching)
		{
			searching = true;
			step = 0;
			status= SearchStatus.SEARCHING;
			if(GUI.isSwarm)
			{
				for (int current = 1; current <= numAgents; current++)
					swarm.add(new Swarm_Agent(current, grid.getStartLocation()));
			}
			else
			{
				agent = new Artificial_Agent(grid.getStartLocation(), vision_range);
			}
			start_time = System.nanoTime();
		}
	}
	public void endSearching()
	{
		if(searching)
		{
			searching = false;
			status = SearchStatus.NOTSEARCHING;
			swarm.clear();
			grid.clearGrid(false);
			agent = null;
		}
	}
	public void setSearchStatus(SearchStatus status)
	{
		this.status = status;
	}
	public void mouseDragged(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	
	public void mouseMoved(MouseEvent e) 
	{
		select_X = e.getX()/20*20;
		select_Y = e.getY()/20*20;
	}
	
	public void mouseClicked(MouseEvent e) 
	{
		if(!searching)
		{
			status = SearchStatus.NOTSEARCHING;
			boolean failed = grid.setEndPoint(select_Y/20, select_X/20, numClicks);
			if (!failed)
				numClicks++;
			if(numClicks > 2)
				numClicks = 0;
		}
	}
	
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	
	public void mousePressed(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	public void mouseReleased(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

}
